MarketCode

huangqimin 5 年之前
父節點
當前提交
4ab2404bea

+ 114 - 0
api/encrypt_views.py

@@ -4,16 +4,26 @@ from __future__ import division
4 4
 
5 5
 import random
6 6
 
7
+from django.conf import settings
8
+from django.db import transaction
7 9
 from django_logit import logit
8 10
 from django_response import response
11
+from pywe_marketcode import tickettocode
12
+from pywe_storage import RedisStorage
9 13
 
14
+from account.models import UserInfo
10 15
 from logs.models import MchInfoDecryptLogInfo, MchInfoEncryptLogInfo
16
+from marketcode.models import MarketCodeInfo
11 17
 from mch.models import ActivityInfo, BrandInfo, ModelInfo
12 18
 from utils.algorithm.b64 import b64_decrypt, b64_encrypt
13 19
 from utils.algorithm.caesar import caesar_decrypt, caesar_encrypt
14 20
 from utils.algorithm.rsalg import rsa_decrypt, rsa_encrypt
21
+from utils.error.errno_utils import MarketCodeStatusCode, UserStatusCode
22
+from utils.redis.connect import r
15 23
 
16 24
 
25
+WECHAT = settings.WECHAT
26
+
17 27
 # CIPHER_ALGORITHM = ('CAESAR', 'B64', 'RSA')
18 28
 CIPHER_ALGORITHM = ('CAESAR', )
19 29
 
@@ -35,6 +45,30 @@ def encrypt(request):
35 45
 
36 46
     mieli, created_at = MchInfoEncryptLogInfo.objects.get_or_create(plaintext=plaintext)
37 47
 
48
+    if settings.KODO_MARKET_CODE_ENABLED:
49
+        if created_at or not mieli.code:
50
+            with transaction.atomic():
51
+                marketcode = MarketCodeInfo.objects.select_for_update().filter(has_used=False).first()
52
+
53
+                if not marketcode:
54
+                    return response(MarketCodeStatusCode.MARKET_CODE_NOT_FOUND)
55
+
56
+                marketcode.has_used = True
57
+                marketcode.save()
58
+
59
+                mieli.code = marketcode.code
60
+                mieli.code_url = marketcode.code_url
61
+                mieli.brand_pk = brand_pk
62
+                mieli.model_pk = model_pk
63
+                mieli.distributor_pk = distributor_pk
64
+                mieli.sn = sn
65
+                mieli.operator_id = optor_id
66
+                mieli.save()
67
+
68
+        return response(200, data={
69
+            'ciphertext': mieli.code_url,
70
+        })
71
+
38 72
     if created_at:
39 73
         alg = random.choice(CIPHER_ALGORITHM)
40 74
 
@@ -160,3 +194,83 @@ def decrypt(request):
160 194
         'redpack_info': elog.redpack_info if elog else {},
161 195
         'integral': model.integral,
162 196
     })
197
+
198
+
199
+@logit(res=True)
200
+def decrypt2(request):
201
+    code_ticket = request.POST.get('code_ticket', '')
202
+    user_id = request.POST.get('user_id', '')
203
+
204
+    try:
205
+        user = UserInfo.objects.get(user_id=user_id)
206
+    except UserInfo.DoesNotExist:
207
+        return response(UserStatusCode.USER_NOT_FOUND)
208
+
209
+    wxcfg = WECHAT.get('JSAPI', {})
210
+
211
+    appid = wxcfg.get('appID')
212
+    secret = wxcfg.get('appsecret')
213
+
214
+    code_info = tickettocode(code_ticket=code_ticket, openid=user.openid_miniapp, appid=appid, secret=secret, token=None, storage=RedisStorage(r))
215
+
216
+    code = code_info.get('code', '')
217
+
218
+    try:
219
+        miel = MchInfoEncryptLogInfo.objects.get(code=code)
220
+    except MchInfoEncryptLogInfo.DoesNotExist:
221
+        return response()
222
+
223
+    plaintext = miel.plaintext
224
+
225
+    # brand_id#model_id#distributor_id#sn#time
226
+    # AAAA#AAAAAA#AAAAA#AAAAAAAAAAAAAA#180224
227
+    brand_pk, model_pk, distributor_pk, sn, time = plaintext.split('#')
228
+
229
+    try:
230
+        brand = BrandInfo.objects.get(pk=brand_pk)
231
+    except BrandInfo.DoesNotExist:
232
+        brand = None
233
+
234
+    try:
235
+        model = ModelInfo.objects.get(pk=model_pk)
236
+    except ModelInfo.DoesNotExist:
237
+        model = None
238
+
239
+    mdli, created_at = MchInfoDecryptLogInfo.objects.get_or_create(ciphertext=ciphertext, defaults={
240
+        'brand_pk': brand_pk,
241
+        'model_pk': model_pk,
242
+        'distributor_pk': distributor_pk,
243
+        'sn': sn,
244
+        'decrypt_count': 1,
245
+    })
246
+
247
+    if not created_at:
248
+        mdli.decrypt_count += 1
249
+        mdli.save()
250
+
251
+    act = ActivityInfo.objects.filter(status=True).order_by('-pk').first()
252
+    has_unexpired_activity = True if act and act.has_unexpired_activity(model.model_uni_name) else False
253
+
254
+    coupon_info = {
255
+        'coupon_expire_at': act.final_coupon_expire_at(created_at=None),
256
+        'coupon_value': act.coupon_value,
257
+    } if has_unexpired_activity else {
258
+        'coupon_expire_at': '',
259
+        'coupon_value': 0,
260
+    }
261
+
262
+    return response(200, data={
263
+        'plaintext': plaintext,
264
+        'logo_url': brand.brand_logo_url if brand else '',
265
+        'model_imgs': model.images if model else [],
266
+        'goodsInfo': {
267
+            'BrandID': brand_pk,
268
+            'Brand': brand.brand_name if brand else '',
269
+            'ModelID': model_pk,
270
+            'Model': (model.model_full_name or model.model_name) if model else '',
271
+            'DistributorID': distributor_pk,
272
+            'SerialNo': sn,
273
+        },
274
+        'has_unexpired_activity': has_unexpired_activity,
275
+        'coupon_info': coupon_info,
276
+    })

+ 1 - 0
api/urls.py

@@ -214,6 +214,7 @@ urlpatterns += [
214 214
 urlpatterns += [
215 215
     url(r'^encrypt$', encrypt_views.encrypt, name='encrypt'),
216 216
     url(r'^decrypt$', encrypt_views.decrypt, name='decrypt'),
217
+    url(r'^decrypt2$', encrypt_views.decrypt2, name='decrypt2'),
217 218
 ]
218 219
 
219 220
 urlpatterns += [

+ 17 - 6
kodo/settings.py

@@ -57,6 +57,7 @@ INSTALLED_APPS = (
57 57
     'guideline',
58 58
     'integral',
59 59
     'logs',
60
+    'marketcode',
60 61
     'mch',
61 62
     'message',
62 63
     'miniapp',
@@ -252,12 +253,12 @@ QINIU = {
252 253
     'secret_key': '05sCekniLCgM6-d_PxrH8sFjvEOsx3ev-FgS7R-k',
253 254
     'bucket_default': 'photo',
254 255
     'buckets': {
255
-        'original': 'http://orf3sfb8s.bkt.clouddn.com',
256
-        'photo': 'http://orf3lnlmb.bkt.clouddn.com',
257
-        'prettify': 'http://orzfu8zxw.bkt.clouddn.com',
258
-        'thumbnail': 'http://orf3ahvt6.bkt.clouddn.com',
259
-        'thumbnail2': 'http://orf3muf5n.bkt.clouddn.com',
260
-        'watermark': 'http://orf3qne9f.bkt.clouddn.com',
256
+        'original': 'http://original.img.pai.ai',
257
+        'photo': 'http://photo.img.pai.ai',
258
+        'prettify': 'http://prettify.img.pai.ai',
259
+        'thumbnail': 'http://thumbnail.img.pai.ai',
260
+        'thumbnail2': 'http://thumbnail2.img.pai.ai',
261
+        'watermark': 'http://watermark.img.pai.ai',
261 262
     }
262 263
 }
263 264
 
@@ -419,6 +420,16 @@ PHONE_2_ADMINISTRATIVE_DIVISION = 'https://www.baifubao.com/callback?cmd=1059&ca
419 420
 
420 421
 TESTING_SNS = ['000000']
421 422
 
423
+COMPONENT_CALLBACK_CONFIG = {
424
+    'tousername': 'brand_id',
425
+}
426
+
427
+# 测试文件
428
+TESTING_ZBAR = os.path.join(BASE_DIR, 'utils/zbar/zbar.jpg').replace('\\', '/')
429
+
430
+# 一物一码设置
431
+KODO_MARKET_CODE_ENABLED = False
432
+
422 433
 # 开发调试相关配置
423 434
 if DEBUG:
424 435
     try:

+ 1 - 1
logs/admin.py

@@ -7,7 +7,7 @@ from logs.models import MchInfoDecryptLogInfo, MchInfoEncryptLogInfo, MchLogInfo
7 7
 
8 8
 
9 9
 class MchInfoEncryptLogInfoAdmin(Readonly2ModelAdmin, admin.ModelAdmin):
10
-    list_display = ('plaintext', 'alg', 'ciphertext', 'brand_pk', 'model_pk', 'distributor_pk', 'sn', 'operator_id', 'is_send_redpack', 'redpack_amount', 'redpack_max_amount', 'has_send_redpack', 'redpack_send_amount', 'user_id', 'nickname', 'is_clerk_send_redpack', 'clerk_redpack_amount', 'clerk_redpack_max_amount', 'has_clerk_send_redpack', 'clerk_redpack_send_amount', 'clerk_user_id', 'clerk_nickname', 'status', 'created_at', 'updated_at')
10
+    list_display = ('plaintext', 'alg', 'ciphertext', 'brand_pk', 'model_pk', 'distributor_pk', 'sn', 'code', 'code_url', 'operator_id', 'is_send_redpack', 'redpack_amount', 'redpack_max_amount', 'has_send_redpack', 'redpack_send_amount', 'user_id', 'nickname', 'is_clerk_send_redpack', 'clerk_redpack_amount', 'clerk_redpack_max_amount', 'has_clerk_send_redpack', 'clerk_redpack_send_amount', 'clerk_user_id', 'clerk_nickname', 'status', 'created_at', 'updated_at')
11 11
     list_filter = ('alg', 'brand_pk', 'model_pk', 'distributor_pk', 'operator_id', 'is_send_redpack', 'has_send_redpack', 'is_clerk_send_redpack', 'has_clerk_send_redpack', 'status')
12 12
     readonly_fields_exclude = ('is_send_redpack', 'redpack_amount', 'redpack_max_amount', 'is_clerk_send_redpack', 'clerk_redpack_amount', 'clerk_redpack_max_amount')
13 13
 

+ 25 - 0
logs/migrations/0014_auto_20200323_0525.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.28 on 2020-03-22 21:25
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('logs', '0013_mchinfoencryptloginfo_user_ids'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='mchinfoencryptloginfo',
17
+            name='code',
18
+            field=models.CharField(blank=True, db_index=True, help_text='\u4e5d\u4f4d\u7684\u5b57\u7b26\u4e32\u539f\u59cb\u7801', max_length=16, null=True, verbose_name='code'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='mchinfoencryptloginfo',
22
+            name='code_url',
23
+            field=models.CharField(blank=True, db_index=True, help_text='28\u4f4d\u666e\u901a\u7801\u5b57\u7b26\t', max_length=128, null=True, verbose_name='code_url'),
24
+        ),
25
+    ]

+ 4 - 0
logs/models.py

@@ -18,6 +18,10 @@ class MchInfoEncryptLogInfo(BaseModelMixin):
18 18
 
19 19
     sn = models.CharField(_(u'sn'), max_length=32, blank=True, null=True, help_text=u'序列号', db_index=True)
20 20
 
21
+    # 一物一码
22
+    code = models.CharField(_(u'code'), max_length=16, blank=True, null=True, help_text=u'九位的字符串原始码', db_index=True)
23
+    code_url = models.CharField(_(u'code_url'), max_length=128, blank=True, null=True, help_text=u'28位普通码字符	', db_index=True)
24
+
21 25
     operator_id = models.CharField(_(u'operator_id'), max_length=32, blank=True, null=True, help_text=u'操作员唯一标识', db_index=True)
22 26
 
23 27
     is_send_redpack = models.BooleanField(_(u'is_send_redpack'), default=False, help_text=u'是否发放红包', db_index=True)

+ 0 - 0
marketcode/__init__.py


+ 12 - 0
marketcode/admin.py

@@ -0,0 +1,12 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.contrib import admin
4
+
5
+from marketcode.models import MarketCodeInfo
6
+
7
+
8
+class MarketCodeInfoAdmin(admin.ModelAdmin):
9
+    list_display = ('isv_application_id', 'application_id', 'code', 'code_url', 'has_used', 'status', 'created_at', 'updated_at')
10
+
11
+
12
+admin.site.register(MarketCodeInfo, MarketCodeInfoAdmin)

+ 8 - 0
marketcode/apps.py

@@ -0,0 +1,8 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.apps import AppConfig
5
+
6
+
7
+class MarketcodeConfig(AppConfig):
8
+    name = 'marketcode'

+ 34 - 0
marketcode/migrations/0001_initial.py

@@ -0,0 +1,34 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2020-01-13 10:32
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    initial = True
11
+
12
+    dependencies = [
13
+    ]
14
+
15
+    operations = [
16
+        migrations.CreateModel(
17
+            name='MarketCodeInfo',
18
+            fields=[
19
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
21
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
22
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
23
+                ('isv_application_id', models.CharField(blank=True, db_index=True, help_text='\u5916\u90e8\u5355\u53f7', max_length=128, null=True, verbose_name='isv_application_id')),
24
+                ('application_id', models.IntegerField(db_index=True, default=0, help_text='\u7533\u8bf7\u5355\u53f7', verbose_name='application_id')),
25
+                ('code', models.CharField(blank=True, db_index=True, help_text='\u4e5d\u4f4d\u7684\u5b57\u7b26\u4e32\u539f\u59cb\u7801', max_length=16, null=True, verbose_name='code')),
26
+                ('code_url', models.CharField(blank=True, db_index=True, help_text='28\u4f4d\u666e\u901a\u7801\u5b57\u7b26\t', max_length=128, null=True, verbose_name='code_url')),
27
+                ('has_used', models.BooleanField(db_index=True, default=True, help_text='\u662f\u5426\u5df2\u4f7f\u7528', verbose_name='has_used')),
28
+            ],
29
+            options={
30
+                'verbose_name': '\u4e00\u7269\u4e00\u7801\u4fe1\u606f',
31
+                'verbose_name_plural': '\u4e00\u7269\u4e00\u7801\u4fe1\u606f',
32
+            },
33
+        ),
34
+    ]

+ 20 - 0
marketcode/migrations/0002_auto_20200113_1851.py

@@ -0,0 +1,20 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2020-01-13 10:51
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('marketcode', '0001_initial'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AlterField(
16
+            model_name='marketcodeinfo',
17
+            name='has_used',
18
+            field=models.BooleanField(db_index=True, default=False, help_text='\u662f\u5426\u5df2\u4f7f\u7528', verbose_name='has_used'),
19
+        ),
20
+    ]

+ 25 - 0
marketcode/migrations/0003_auto_20200114_0220.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2020-01-13 18:20
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('marketcode', '0002_auto_20200113_1851'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='marketcodeinfo',
17
+            name='code_index',
18
+            field=models.IntegerField(default=0, help_text='\u8be5\u7801\u5728\u6279\u6b21\u4e2d\u7684\u504f\u79fb\u91cf\t', verbose_name='code_index'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='marketcodeinfo',
22
+            name='lattice',
23
+            field=models.CharField(blank=True, help_text='361 \u5b57\u8282\u7684 01 \u70b9\u9635\uff0c\u7528\u4e8e\u652f\u6301\u751f\u6210 19 * 19 \u7684\u5fae\u578b\u7801\uff0c0 \u4e3a\u767d\uff0c1 \u4e3a\u9ed1', max_length=361, null=True, verbose_name='code'),
24
+        ),
25
+    ]

+ 0 - 0
marketcode/migrations/__init__.py


+ 24 - 0
marketcode/models.py

@@ -0,0 +1,24 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import models
4
+from django.utils.translation import ugettext_lazy as _
5
+from django_models_ext import BaseModelMixin
6
+
7
+
8
+class MarketCodeInfo(BaseModelMixin):
9
+    isv_application_id = models.CharField(_(u'isv_application_id'), max_length=128, blank=True, null=True, help_text=u'外部单号', db_index=True)
10
+    application_id = models.IntegerField(_(u'application_id'), default=0, help_text=u'申请单号', db_index=True)
11
+
12
+    lattice = models.CharField(_(u'code'), max_length=361, blank=True, null=True, help_text=u'361 字节的 01 点阵,用于支持生成 19 * 19 的微型码,0 为白,1 为黑')
13
+    code = models.CharField(_(u'code'), max_length=16, blank=True, null=True, help_text=u'九位的字符串原始码', db_index=True)
14
+    code_index = models.IntegerField(_(u'code_index'), default=0, help_text=u'该码在批次中的偏移量	')
15
+    code_url = models.CharField(_(u'code_url'), max_length=128, blank=True, null=True, help_text=u'28位普通码字符	', db_index=True)
16
+
17
+    has_used = models.BooleanField(_(u'has_used'), default=False, help_text=_(u'是否已使用'), db_index=True)
18
+
19
+    class Meta:
20
+        verbose_name = _(u'一物一码信息')
21
+        verbose_name_plural = _(u'一物一码信息')
22
+
23
+    def __unicode__(self):
24
+        return unicode(self.pk)

+ 6 - 0
marketcode/tests.py

@@ -0,0 +1,6 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.test import TestCase
5
+
6
+# Create your tests here.

+ 6 - 0
marketcode/views.py

@@ -0,0 +1,6 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.shortcuts import render
5
+
6
+# Create your views here.

+ 41 - 0
pre/market_code.py

@@ -0,0 +1,41 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf import settings
4
+from pywe_marketcode import applycodedownload
5
+from pywe_storage import RedisStorage
6
+
7
+from marketcode.models import MarketCodeInfo
8
+from utils.redis.connect import r
9
+
10
+
11
+WECHAT = settings.WECHAT
12
+
13
+
14
+def marketcodedownload(application_id, code_start, code_end, isv_application_id=''):
15
+    wxcfg = WECHAT.get('JSAPI', {})
16
+
17
+    appid = wxcfg.get('appID')
18
+    secret = wxcfg.get('appsecret')
19
+
20
+    codes = applycodedownload(application_id=application_id, code_start=code_start, code_end=code_end, appid=appid, secret=secret, token=None, storage=RedisStorage(r))
21
+
22
+    lattice, code, code_index, code_url = ''
23
+    for idx, item in enumerate(codes):
24
+        if idx % 4 == 0:
25
+            lattice = item
26
+        if idx % 4 == 1:
27
+            code = item
28
+        if idx % 4 == 2:
29
+            code_index = item
30
+        if idx % 4 == 3:
31
+            code_url = item
32
+            MarketCodeInfo.objects.create(
33
+                isv_application_id=isv_application_id,
34
+                application_id=application_id,
35
+                lattice=lattice,
36
+                code=code,
37
+                code_index=code_index,
38
+                code_url=code_url
39
+            )
40
+
41
+

+ 1 - 0
requirements_pywe.txt

@@ -2,6 +2,7 @@ pywe-card==1.0.0
2 2
 pywe-component==1.0.1
3 3
 pywe-component-preauthcode==1.0.3
4 4
 pywe-jssdk==1.1.0
5
+pywe-marketcode==1.0.1
5 6
 pywe-membercard==1.0.3
6 7
 pywe-miniapp==1.1.5
7 8
 pywe-oauth==1.0.7

+ 33 - 0
utils/error/errno_utils.py

@@ -59,6 +59,39 @@ class ProductStatusCode(BaseStatusCode):
59 59
     PRODUCT_NOT_USED = StatusCodeField(502012, 'Product Not Used', description=u'产品未使用')
60 60
 
61 61
 
62
+class MemberGoodStatusCode(BaseStatusCode):
63
+    """ 会员商品相关错误码 5035xx """
64
+    GOOD_NOT_FOUND = StatusCodeField(503501, 'Good Not Found', description=u'商品不存在')
65
+
66
+    GOOD_NO_EXCHANGE_PERMISSION = StatusCodeField(503502, 'Good No Exchange Permission', description=u'商品无兑换权限')
67
+    GOOD_INTEGRAL_NOT_ENOUGH = StatusCodeField(503503, 'Good Integral Not Enough', description=u'商品兑换积分不足')
68
+    GOOD_STOCK_NOT_ENOUGH = StatusCodeField(503504, 'Good Integral Not Enough', description=u'商品库存不足')
69
+
70
+
71
+class MemberRightStatusCode(BaseStatusCode):
72
+    """ 会员商品相关错误码 5036xx """
73
+    RIGHT_NOT_FOUND = StatusCodeField(503601, 'Right Not Found', description=u'权益不存在')
74
+
75
+
76
+class MemberActivityStatusCode(BaseStatusCode):
77
+    """ 会员活动相关错误码 5037xx """
78
+    ACTIVITY_NOT_FOUND = StatusCodeField(503701, 'Activity Not Found', description=u'活动不存在')
79
+
80
+
81
+class MemberCouponStatusCode(BaseStatusCode):
82
+    """ 会员优惠券相关错误码 5040xx """
83
+    USER_COUPON_NOT_FOUND = StatusCodeField(504001, 'User Coupon Not Found', description=u'用户优惠券不存在')
84
+
85
+    USER_COUPON_HAS_USED = StatusCodeField(504010, 'User Coupon Has Used', description=u'用户优惠券已使用')
86
+    USER_COUPON_NOT_ACTIVED = StatusCodeField(504011, 'User Coupon Not Actived', description=u'用户优惠券未生效')
87
+    USER_COUPON_HAS_EXPIRED = StatusCodeField(504012, 'User Coupon Has Expired', description=u'用户优惠券已过期')
88
+
89
+
90
+class MarketCodeStatusCode(BaseStatusCode):
91
+    """ 一物一码相关错误码 5050xx """
92
+    MARKET_CODE_NOT_FOUND = StatusCodeField(505001, 'Market Code Not Found', description=u'一物一码不存在')
93
+
94
+
62 95
 class LensmanStatusCode(BaseStatusCode):
63 96
     """ 摄影师相关错误码 4000xx """
64 97
     LENSMAN_NOT_FOUND = StatusCodeField(400001, 'Lensman Not Found', description=u'摄影师不存在')